
#include <hk32f030m.h>
#include <hk32f030m_gpio.h>
#include <hk32f030m_rcc.h>
#include <perf_counter.h>

#include <pin/cm_hk030.hpp>
#include <scheduler_basic.hpp>
#include <scheduler_tick.hpp>


void nrst_switch() {
    // 切换NRST 为PA0
    RCC->APB1ENR |= RCC_APB1ENR_IOMUXEN;
    GPIOMUX->NRST_PIN_KEY = 0x5AE1;
    GPIOMUX->NRST_PA0_SEL = 1;
}


/* ================================== 数码管引脚定义 ======================================= */

constexpr size_t DIG_COUNT = 5;
constexpr size_t SEG_COUNT = 8;

// 数码管对应8 + 5 一共13 个引脚，全都是推挽输出模式，
// 用union 把seg 引脚和dig 引脚的数组连到一个长度13 的数组all_pin 里
// 方便批量设置GPIO 模式
const union {
    ioxx::Pin all_pin[SEG_COUNT + DIG_COUNT];

    struct {
        ioxx::Pin seg[SEG_COUNT];
        ioxx::Pin dig[DIG_COUNT];
    } pin;

} TUBE = {
    .pin = {

        // ======== 共阴数码管8 段引脚 ==========
        // 用一个数组把八个段引脚装一起方便处理
        // 8 段对应一个字节，按顺序，A 是LSB，DP 是MSB
        // 所以SEG_A 在数组里是是[0]，
        .seg = {
            ioxx::Pin(GPIOD, 1),  // 数码管SEG A 引脚，连接到阳极
            ioxx::Pin(GPIOC, 7),  // SEG B
            ioxx::Pin(GPIOC, 6),  // SEG C
            ioxx::Pin(GPIOC, 5),  // SEG D
            ioxx::Pin(GPIOC, 4),  // SEG E
            ioxx::Pin(GPIOC, 3),  // SEG F
            ioxx::Pin(GPIOB, 4),  // SEG G
            ioxx::Pin(GPIOD, 6),  // 小数点 DP
        },

        // ======= 共阴数码管5 位COM 引脚 =============
        // 连接到数码管阴极，所以低电平有效
        // 同样装在一个数组里，DIG0 对应[0]
        .dig = {
            ioxx::Pin(GPIOA, 0),  // DIG0
            ioxx::Pin(GPIOA, 1),  // DIG1
            ioxx::Pin(GPIOA, 2),  // DIG2
            ioxx::Pin(GPIOA, 3),  // DIG3
            ioxx::Pin(GPIOD, 4),  // DIG4
        }}};


/* ================================== 数码管引脚操作 ======================================= */

/** 初始化所有数码管引脚为输出模式
 */
void init_tube_pin() {
    using namespace ioxx;
    PinInit pi;
    pi.mode(mode::out)
        .drive(drive::push_pull);

    for (const Pin& p : TUBE.all_pin) {
        pi.init(p);
    }
}


using TimeSource = scheduler_basic::PerfCounterMsSource;
using TimeType = TimeSource::TimeType;


/** 初始化代码
 */
void setup() {
    // "在使用进入 stop 低功耗模式时，一定要记得将 PWR 时钟打开"
    // "没有打开 PWR 时钟，则会功能异常，且不能再次烧录程序"
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);

    // 启动SysTick
    NVIC_SetPriority(SysTick_IRQn, 1);
    NVIC_EnableIRQ(SysTick_IRQn);
    // 初始化perf_counter
    init_cycle_counter(false);
    // 启动中断
    __enable_irq();

    // 延时100ms，因为复用了NRST 引脚，可能导致烧程序失败
    delay_ms(100);
    ioxx::enable_gpio();
    nrst_switch();

    // 数码管引脚初始化
    init_tube_pin();
}


int main(void) {
    setup();

    using namespace scheduler_basic;

    for (const auto& p : TUBE.pin.seg) {
        p.set();
    }

    TimeCycle<TimeSource> c;
    int i = 0;

    while (1) {
        if (c.cycle(200)) {
            TUBE.pin.dig[i].toggle();
            i++;
            if (i == DIG_COUNT) {
                i = 0;
            }
        }
    }
}


extern "C" {
// 如果中断函数直接放在cpp 文件，由于C++ 编译器会修改函数名，就和中断向量表的命名不匹配了


__attribute__((used)) void SysTick_Handler() {
    user_code_insert_to_systick_handler();
}
}


#ifdef USE_FULL_ASSERT
/**
 * @brief  Reports the name of the source file and the source line number
 *         where the assert_param error has occurred.
 * @param  file: pointer to the source file name
 * @param  line: assert_param error line source number
 * @retval None
 */
void assert_failed(char* file, uint32_t line) {
    /* User can add his own implementation to report the file name and line number,
       tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* Infinite loop */

    while (1) {
    }
}
#endif /* USE_FULL_ASSERT */
